﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Security;
using System.Collections.Specialized;
using System.Web.Configuration;
using System.Reflection;
using System.Configuration.Provider;

namespace NMigrate
{
	public class SqlMembershipProviderFactory
	{
		class Property
		{
			private PropertyInfo property;
			private SqlMembershipProviderFactory obj;
			ConfigValueAttribute _Attribute;

			public Property(PropertyInfo property, SqlMembershipProviderFactory obj)
			{
				this.property = property;
				this.obj = obj;
				_Attribute = (ConfigValueAttribute)property.GetCustomAttributes(typeof(ConfigValueAttribute), true).First();
			}

			public string Key
			{
				get
				{
					return _Attribute.KeyString;
				}
			}

			public string Value
			{
				get
				{

					if(IsNullableProperty)
					{
						var nullableResult = property.GetValue(obj, null);
						if(nullableResult == null)
							return null;
						var result = property.PropertyType.GetProperty("Value").GetValue(nullableResult, null);
						if(result == null)
							return null;
						return result.ToString();
					}
					else
					{
						var result = property.GetValue(obj, null);
						if(result == null)
							return null;
						return result.ToString();
					}
				}
			}

			public bool HasValue
			{
				get
				{
					return Value != null;
				}
			}

			public bool IsNullableProperty
			{
				get
				{
					return property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
				}
			}

			public bool IsBool
			{
				get
				{
					return IsNullable(typeof(bool)) || property.PropertyType == typeof(bool);
				}
			}
			public bool IsInt
			{
				get
				{
					return IsNullable(typeof(int)) || property.PropertyType == typeof(int);
				}
			}

			private bool IsNullable(Type type)
			{
				return IsNullableProperty && property.PropertyType.GetGenericArguments()[0] == type;
			}
		}
		[AttributeUsage(AttributeTargets.Property)]
		public class ConfigValueAttribute : Attribute
		{
			public ConfigValueAttribute(string keyString)
			{
				_KeyString = keyString;
			}
			private readonly string _KeyString;
			public string KeyString
			{
				get
				{
					return _KeyString;
				}
			}
		}

		public SqlMembershipProviderFactory()
		{
			ApplicationName = "App";
		}

		public SqlMembershipProvider CreateMembershipProvider()
		{
			var provider = new SqlMembershipProvider();
			var valueCollection = FillNameValueCollection(new System.Collections.Specialized.NameValueCollection());
			var providerName = GetProviderName();
			provider.Initialize(providerName, valueCollection);
			return provider;
		}

		private string GetProviderName()
		{
			var provider = Membership.Providers.OfType<ProviderBase>().FirstOrDefault();
			if(provider == null)
				throw new InvalidOperationException("Because of internal implementation of SqlMembershipProvider, to make the factory work, you have to add at list one membership provider in your config file (you can add any membership you want, it does not matter since the provider in the config file will not be used)");
			return provider.Name;
		}
		

		private NameValueCollection FillNameValueCollection(NameValueCollection nameValueCollection)
		{
			foreach(var property in this.GetType().GetProperties().Where(p => p.IsDefined(typeof(ConfigValueAttribute), true)).Select(p => new Property(p, this)))
			{
				if(property.IsInt || property.IsBool)
				{
					if(property.IsNullableProperty)
					{
						if(property.HasValue)
						{
							nameValueCollection.Add(property.Key, property.Value);
						}
					}
				}
				else
				{
					if(property.Value != null)
						nameValueCollection.Add(property.Key, property.Value);
				}
			}
			return nameValueCollection;
		}

		public void SetLowSecurity()
		{
			EnablePasswordRetrieval = false;
			EnablePasswordReset = true;
			RequiresQuestionAndAnswer = false;
			RequiresUniqueEmail = false;
			PasswordFormat = MembershipPasswordFormat.Hashed;
			MaxInvalidPasswordAttempts = 10;
			MinRequiredNonalphanumericCharacters = 0;
			MinRequiredPasswordLength = 4;
			PasswordAttemptWindow = 10;
		}
		[ConfigValue("connectionStringName")]
		public string ConnectionStringName
		{
			get;
			set;
		}
		[ConfigValue("connectionString")]
		public string ConnectionString
		{
			get;
			set;
		}
		[ConfigValue("enablePasswordRetrieval")]
		public bool? EnablePasswordRetrieval
		{
			get;
			set;
		}
		[ConfigValue("enablePasswordReset")]
		public bool? EnablePasswordReset
		{
			get;
			set;
		}
		[ConfigValue("requiresQuestionAndAnswer")]
		public bool? RequiresQuestionAndAnswer
		{
			get;
			set;
		}
		[ConfigValue("applicationName")]
		public string ApplicationName
		{
			get;
			set;
		}
		[ConfigValue("requiresUniqueEmail")]
		public bool? RequiresUniqueEmail
		{
			get;
			set;
		}
		[ConfigValue("maxInvalidPasswordAttempts")]
		public int? MaxInvalidPasswordAttempts
		{
			get;
			set;
		}
		[ConfigValue("passwordAttemptWindow")]
		public int? PasswordAttemptWindow
		{
			get;
			set;
		}
		[ConfigValue("commandTimeout")]
		public int? CommandTimeout
		{
			get;
			set;
		}
		[ConfigValue("passwordFormat")]
		public MembershipPasswordFormat? PasswordFormat
		{
			get;
			set;
		}

		[ConfigValue("name")]
		public string Name
		{
			get;
			set;
		}
		[ConfigValue("minRequiredPasswordLength")]
		public int? MinRequiredPasswordLength
		{
			get;
			set;
		}
		[ConfigValue("minRequiredNonalphanumericCharacters")]
		public int? MinRequiredNonalphanumericCharacters
		{
			get;
			set;
		}
		[ConfigValue("passwordStrengthRegularExpression")]
		public string PasswordStrengthRegularExpression
		{
			get;
			set;
		}
		[ConfigValue("passwordCompatMode")]
		public MembershipPasswordCompatibilityMode? PasswordCompatMode
		{
			get;
			set;
		}
	}
}
